﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;

namespace Smarly.Ebook.BussinesLayer
{
	public abstract class Element
	{
		public static readonly XNamespace aid = XNamespace.Get("http://ns.adobe.com/AdobeInDesign/4.0/");
		public static readonly XNamespace aid5 = XNamespace.Get("http://ns.adobe.com/AdobeInDesign/5.0/");

		protected static Tag Tag(XElement el)
		{
			return (Tag)Enum.Parse(typeof(Tag), el.Name.ToString());
		}

		protected static string Tag(Tag tag)
		{
			return Enum.GetName(typeof(Tag), tag);
		}

		protected static void RemoveId(XElement el)
		{
			XAttribute idAttr;
			if ((idAttr = el.Attribute("id")) != null)
				idAttr.Remove();
		}

		protected static void AddParagraph(XElement el)
		{
			if (el.Value.EndsWith("\r") || el.Value.EndsWith("\n"))
				return;
			el.Add(new XElement("EndLine", "\r"));
		}

		protected static void AddParagraphStyle(XElement el, string styleName = null)
		{
			el.SetAttributeValue(XName.Get("pstyle", aid.NamespaceName), styleName ?? (/*"." + */el.Name));
		}

		protected static void AddCharacterStyle(XElement el, string styleName = null)
		{
			el.SetAttributeValue(XName.Get("cstyle", aid.NamespaceName), styleName ?? (/*"." + */el.Name));
		}

		public static void Parse(XElement original, XElement parent)
		{
			var tag = Tag(original);
			switch (tag)
			{
				case Ebook.Tag.blockquote:
					Blockquote.Create(original, parent);
					break;
				case Ebook.Tag.div:
					Div.Create(original, parent);
					break;
				case Ebook.Tag.p:
					P.Create(original, parent);
					break;
				case Ebook.Tag.pre:
					Pre.Create(original, parent);
					break;
				case Ebook.Tag.ul:
					Ul.Create(original, parent);
					break;
				case Ebook.Tag.ol:
					Ol.Create(original, parent);
					break;
				case Ebook.Tag.h3:
					H3.Create(original, parent);
					break;
				case Ebook.Tag.h4:
					H4.Create(original, parent);
					break;
				case Ebook.Tag.h5:
					H5.Create(original, parent);
					break;
				case Ebook.Tag.h6:
					H6.Create(original, parent);
					break;
				case Ebook.Tag.img:
					Img.Create(original, parent);
					break;
				case Ebook.Tag.p_left:
					PLeft.Create(original, parent);
					break;
				case Ebook.Tag.table:
					Table.Create(original, parent);
					break;
/*
				case Ebook.Tag.TheadTd:
					TheadTd.Create(original, parent);
					break;
*/
/*
				case Ebook.Tag.TbodyTd:
					TbodyTd.Create(original, parent);
					break;
*/
				default:
					throw new ArgumentOutOfRangeException("###Parse: " + tag + "###");
			}
		}

		public static void ParseCharacter(XElement element)
		{
			foreach (var el in element.Elements())
			{
				var tag = Tag(el);
				switch (tag)
				{
					case Ebook.Tag.em:
					case Ebook.Tag.i:
						Italic.Create(el);
						break;
					case Ebook.Tag.b:
					case Ebook.Tag.strong:
						Bold.Create(el);
						break;
					case Ebook.Tag.code:
						Code.Create(el);
						break;
					case Ebook.Tag.a:
						//todo
						break;
					default:
						throw new ArgumentOutOfRangeException("###Parse Character: " + tag + "###");
				}
			}
		}
	}

	#region Character

	public class Code : Element
	{
		public static void Create(XElement element)
		{
			RemoveId(element);
			AddCharacterStyle(element);
		}
	}

	public class Italic : Element
	{
		public static void Create(XElement element)
		{
			RemoveId(element);
			element.Name = Element.Tag(Ebook.Tag.em);
			AddCharacterStyle(element);
		}
	}

	public class Bold : Element
	{
		public static void Create(XElement element)
		{
			RemoveId(element);
			element.Name = Element.Tag(Ebook.Tag.strong);
			AddCharacterStyle(element);
		}
	}

	#endregion

	#region Paragraphs

	public class Img : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement("Image");

			var name = original.Attribute("src").Value.Split('/').Last();
			current.Add(new XAttribute("href", "file://d:/Work/Layout/Metaprogramming-in-NET-2012-12/Chapter1/img/fig." + name));

			parent.Add(current);
		}
	}

	public class H3 : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraph(current);
			AddParagraphStyle(current);
		}
	}

	public class H4 : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraph(current);
			AddParagraphStyle(current);
		}
	}

	public class H5 : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraph(current);
			AddParagraphStyle(current);
		}
	}

	public class H6 : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraph(current);
			AddParagraphStyle(current);
		}
	}

	public class P : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraph(current);
			AddParagraphStyle(current);
		}
	}

/*	public class TheadTd : Element
	{
		public static void Create(XElement cell, XElement parent)
		{
/*
			Parse(cell.Element(Tag(Ebook.Tag.p_left)), cell);
			parent.Add(cell);
#1#
		}
	}*/

/*
	public class TbodyTd : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			original.SetAttributeValue(XName.Get("crows", aid.NamespaceName), "1");
			original.SetAttributeValue(XName.Get("ccols", aid.NamespaceName), "1");
			original.SetAttributeValue(XName.Get("table", aid.NamespaceName), "cell");
			original.SetAttributeValue(XName.Get("cellstyle", aid5.NamespaceName), ".tbody");
			Parse(original.Element(Tag(Ebook.Tag.p_left)), original);
			parent.Add(original);
		}
	}
*/

	public class PLeft : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var current = new XElement(original);
			RemoveId(current);
			parent.Add(current);
			ParseCharacter(current);

			AddParagraphStyle(current);
		}
	}

	public class Ul : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			foreach (var el in original.Elements())
			{
				var ch = new XElement(el);
				ParseCharacter(ch);

				AddParagraphStyle(ch, "ul");
				AddParagraph(ch);

				parent.Add(ch);

			}
		}
	}

	public class Ol : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			foreach (var el in original.Elements())
			{
				var ch = new XElement(el);
				ParseCharacter(ch);

				AddParagraphStyle(ch, "ol");
				AddParagraph(ch);

				parent.Add(ch);

			}
		}
	}

	public class Pre : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var code = new XElement(original.Elements().First());
			code.Name = "pre";
			RemoveId(code);
			parent.Add(code);

			foreach (var ch in code.Elements())
			{
				ParseCharacter(ch);
			}

			AddParagraph(code);
			AddParagraphStyle(code, "listing");
		}
	}

	public class Div : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			var blocks = new string[] {"listing", "img", "table"};
			IEnumerable<XElement> elements = original.Elements();
			string type = string.Empty;

			if (elements != null)
			{
				if (elements.Any(x => x.Name.ToString().ToLower() == "pre"))
				{
					type = "listing";
				}
				else if (elements.Any(x => x.Name.ToString().ToLower() == "img"))
				{
					type = "img";
				}
				else if (elements.Any(x => x.Name.ToString().ToLower() == "table"))
				{
					type = "table";
				}
			}

			foreach (var child in original.Elements())
			{
				XElement ch = child;
				if (blocks.Contains(type) && Tag(child) == Ebook.Tag.blockquote)
				{
					ch = new XElement(Tag(Ebook.Tag.blockquote));
					var clone = new XElement(child);
					clone.Name = Tag(Ebook.Tag.p_left);
					ch.Add(clone);
				}
				Parse(ch, parent);
			}
		}
	}

	public class Table : Element
	{
		public static void Create(XElement original, XElement parent)
		{
			XElement table = new XElement(parent.Name);
			table.SetAttributeValue(XName.Get("table", aid.NamespaceName), "table");
			table.SetAttributeValue(XName.Get("tablestyle", aid5.NamespaceName), ".table");

			var rows = 0;
			var colls = 0;

			foreach (var th in original.Elements())
			{
				switch (Tag(th))
				{
					case Ebook.Tag.thead:
						foreach (var thtr in th.Elements())
						{
							rows++;
							var tempColls = 0;
							foreach (var td in thtr.Elements())
							{
								tempColls++;
								var cell = new XElement("cell");

								cell.SetAttributeValue(XName.Get("crows", aid.NamespaceName), "1");
								cell.SetAttributeValue(XName.Get("ccols", aid.NamespaceName), "1");
								cell.SetAttributeValue(XName.Get("table", aid.NamespaceName), "cell");
								cell.SetAttributeValue(XName.Get("cellstyle", aid5.NamespaceName), ".thead");

								var text = new XElement(Tag(Ebook.Tag.p_left));

								text.Add(new XElement(td)
								{
									Name = Tag(Ebook.Tag.strong)
								});

								Parse(text, cell);
								table.Add(cell);
							}
							colls = tempColls > colls ? tempColls : colls;
						}
						break;
					case Ebook.Tag.tbody:
						foreach (var thtr in th.Elements())
						{
							rows++;
							var tempColls = 0;
							foreach (var td in thtr.Elements())
							{
								tempColls++;
								var cell = new XElement("cell");

								cell.SetAttributeValue(XName.Get("crows", aid.NamespaceName), "1");
								cell.SetAttributeValue(XName.Get("ccols", aid.NamespaceName), "1");
								cell.SetAttributeValue(XName.Get("table", aid.NamespaceName), "cell");
								cell.SetAttributeValue(XName.Get("cellstyle", aid5.NamespaceName), ".tbody");

								var text = new XElement(td)
								{
									Name = Tag(Ebook.Tag.p_left)
								};
								Parse(text, cell);
								table.Add(cell);
							}
							colls = tempColls > colls ? tempColls : colls;
						}
						break;
					default:
						throw new ArgumentOutOfRangeException();
				}
			}

						table.SetAttributeValue(XName.Get("tcols", aid.NamespaceName), colls);
						table.SetAttributeValue(XName.Get("trows", aid.NamespaceName), rows);


			parent.Add(table);
		}
	}

	public class Blockquote : Element
	{
		public static void Create(XElement _original, XElement _parent)
		{
			var rows = _original.Elements().ToList();

			#region Table Properties

			XElement table = new XElement(_parent.Name);
			table.SetAttributeValue(XName.Get("table", aid.NamespaceName), "table");
			table.SetAttributeValue(XName.Get("tcols", aid.NamespaceName), "2");
			table.SetAttributeValue(XName.Get("tablestyle", aid5.NamespaceName), ".blockquote");
			table.SetAttributeValue(XName.Get("trows", aid.NamespaceName), rows.Count.ToString(CultureInfo.InvariantCulture));

			#endregion

			foreach (var cell in rows)
			{
				#region Left BlockQuote Cell

				var leftElement = new XElement("LeftCell");
				leftElement.SetAttributeValue(XName.Get("crows", aid.NamespaceName), "1");
				leftElement.SetAttributeValue(XName.Get("ccols", aid.NamespaceName), "1");
				leftElement.SetAttributeValue(XName.Get("table", aid.NamespaceName), "cell");
				leftElement.SetAttributeValue(XName.Get("cellstyle", aid5.NamespaceName), ".blockquote left");
				table.Add(leftElement);

				#endregion

				#region Right BlockQuote Cell

				var rightElement = new XElement("RightCell");
				rightElement.SetAttributeValue(XName.Get("crows", aid.NamespaceName), "1");
				rightElement.SetAttributeValue(XName.Get("ccols", aid.NamespaceName), "1");
				rightElement.SetAttributeValue(XName.Get("table", aid.NamespaceName), "cell");
				rightElement.SetAttributeValue(XName.Get("ccolwidth", aid.NamespaceName), "392");
				rightElement.SetAttributeValue(XName.Get("cellstyle", aid5.NamespaceName), ".blockquote right");
				rightElement.Add();

				Parse(cell, rightElement);

				table.Add(rightElement);

				#endregion
			}

			_parent.Add(table);
		}
	}

	#endregion
}